package com.yunda.nebula.gateway.service.impl;

import com.alibaba.nacos.common.util.Md5Utils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yunda.nebula.core.tool.utils.BeanUtil;
import com.yunda.nebula.gateway.core.ListenerDto;
import com.yunda.nebula.gateway.core.consts.ModifyType;
import com.yunda.nebula.gateway.core.entity.*;
import com.yunda.nebula.gateway.mapper.TbGatewayAppMapper;
import com.yunda.nebula.gateway.service.ITbGatewayAppService;
import com.yunda.nebula.gateway.service.ITbGatewayFilterService;
import com.yunda.nebula.gateway.service.ITbGatewayPredicatesService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * <p>
 * app 服务实现类
 * </p>
 *
 * @author zhiguo.dong
 * @since 2020-09-17
 */
@Service
public class TbGatewayAppServiceImpl extends ServiceImpl<TbGatewayAppMapper, TbGatewayApp> implements ITbGatewayAppService {

    @Autowired
    private ITbGatewayFilterService tbGatewayFilterService;

    @Autowired
    private ITbGatewayPredicatesService tbGatewayPredicatesService;

    @Override
    public List<String> listApps() {
        LambdaQueryWrapper<TbGatewayApp> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TbGatewayApp::getStatus, "1");
        List<TbGatewayApp> tbGatewayApps = this.list(wrapper);
        List<String> appIdList = tbGatewayApps.stream().filter(tbGatewayApp -> {
            int count = tbGatewayPredicatesService.count(new LambdaQueryWrapper<TbGatewayPredicates>().eq(TbGatewayPredicates::getAppId, tbGatewayApp.getAppId()));
            return count == 1;
        }).map(TbGatewayApp::getAppId).collect(Collectors.toList());

        return appIdList;
    }

    @Override
    public ConfigData fetch(String appId) {
        ConfigData configData = new ConfigData();
        configData.setAppId(appId);


        TbGatewayApp tbGatewayApp = baseMapper.selectOne(new LambdaQueryWrapper<TbGatewayApp>().eq(TbGatewayApp::getAppId, appId));
        TbGatewayPredicates tbGatewayPredicates = tbGatewayPredicatesService.getOne(new LambdaQueryWrapper<TbGatewayPredicates>().eq(TbGatewayPredicates::getAppId, appId));
        List<TbGatewayFilter> tbGatewayFilters = tbGatewayFilterService.list(new LambdaQueryWrapper<TbGatewayFilter>().eq(TbGatewayFilter::getAppId, appId));

        if (Objects.isNull(tbGatewayApp) || Objects.isNull(tbGatewayPredicates)) {
            return null;
        }

        AppInfo appInfo = BeanUtil.copy(tbGatewayApp, AppInfo.class);
        PathInfo pathInfo = BeanUtil.copy(tbGatewayPredicates, PathInfo.class);
        List<FilterInfo> filterInfoList = BeanUtil.copy(tbGatewayFilters, FilterInfo.class);
        configData.setAppInfo(appInfo);
        configData.setPathInfo(pathInfo);
        configData.setFilterInfos(filterInfoList);

        Long modifyTimestamp = getLastTimeStamp(tbGatewayApp, tbGatewayPredicates, tbGatewayFilters);


        configData.setLastModifyTime(modifyTimestamp);
        String md5 = Md5Utils.getMD5((appId + modifyTimestamp).getBytes());
        configData.setMd5(md5);

        return configData;
    }

    @Override
    public FetchAppInfo listener(ListenerDto listenerDto) {
        FetchAppInfo fetchAppInfo = new FetchAppInfo();
        fetchAppInfo.setAppId(listenerDto.getAppId());

        TbGatewayApp tbGatewayApp = baseMapper.selectOne(new LambdaQueryWrapper<TbGatewayApp>().eq(TbGatewayApp::getAppId, listenerDto.getAppId())
        .eq(TbGatewayApp::getStatus,"1"));

        TbGatewayPredicates tbGatewayPredicates = tbGatewayPredicatesService.getOne(new LambdaQueryWrapper<TbGatewayPredicates>().eq(TbGatewayPredicates::getAppId, listenerDto.getAppId()) );
        List<TbGatewayFilter> tbGatewayFilters = tbGatewayFilterService.list(new LambdaQueryWrapper<TbGatewayFilter>().eq(TbGatewayFilter::getAppId, listenerDto.getAppId()));

        if (Objects.isNull(tbGatewayApp) || Objects.isNull(tbGatewayPredicates)) {
            fetchAppInfo.setType(ModifyType.delete);
            return fetchAppInfo;
        }

        Long modifyTimestamp = getLastTimeStamp(tbGatewayApp, tbGatewayPredicates, tbGatewayFilters);
        if (modifyTimestamp > listenerDto.getTimestamp()) {
            fetchAppInfo.setType(ModifyType.update);
            return fetchAppInfo;
        }
        return null;

    }

    private Long getLastTimeStamp(TbGatewayApp tbGatewayApp, TbGatewayPredicates tbGatewayPredicates, List<TbGatewayFilter> tbGatewayFilters) {
        List<LocalDateTime> dateTimeList = new ArrayList<>();
        dateTimeList.add(tbGatewayApp.getUpdateTime());
        dateTimeList.add(tbGatewayPredicates.getUpdateTime());
        tbGatewayFilters.forEach(tbGatewayFilter -> dateTimeList.add(tbGatewayFilter.getUpdateTime()));

        return dateTimeList.stream().sorted(Comparator.reverseOrder()).mapToLong(localDateTime -> Timestamp.valueOf(localDateTime).getTime()).findFirst().getAsLong();

    }


}
